package main

/**
上节课我为你讲解了如何通过 Context 更好地控制多个协程，课程最后的思考题是：如何通过 Context 实现日志跟踪？
要想跟踪一个用户的请求，必须有一个唯一的 ID 来标识这次请求调用了哪些函数、执行了哪些代码，然后通过这个唯一的 ID 把日志信息串联起来。这样就形成了一个日志轨迹，也就实现了用户的跟踪，于是思路就有了。
在用户请求的入口点生成 TraceID。
通过 context.WithValue 保存 TraceID。
然后这个保存着 TraceID 的 Context 就可以作为参数在各个协程或者函数间传递。
在需要记录日志的地方，通过 Context 的 Value 方法获取保存的 TraceID，然后把它和其他日志信息记录下来。
这样具备同样 TraceID 的日志就可以被串联起来，达到日志跟踪的目的。
以上思路实现的核心是 Context 的传值功能。
目前我们已熟练掌握了 goroutine、channel、sync 包的同步原语，这些都是并发编程比较基础的元素。而这节课要介绍的是如何用这些基础元素组成并发模式，帮助我们更好地编写并发程序。
*/

// for select 循环模式
/**
多路复用
for { //for无限循环，或者for range循环
  select {
    //通过一个channel控制
  }
}
这是一种 for 循环 +select 多路复用的并发模式，哪个 case 满足就执行哪个，直到满足一定的条件退出 for 循环（比如发送退出信号）。
*/
// for select 无限循环
/**
for  {
   select {
   case <-done:
      return
   default:
      //执行具体的任务
   }
 }
这种模式也会有一个 done channel，用于退出当前的 for 循环，而另外一个 resultCh channel 用于接收 for range 循环的值，这些值通过 resultCh 可以传送给其他的调用者。
*/
// select timeout 模式
/**
假如需要访问服务器获取数据，因为网络的不同响应时间不一样，为保证程序的质量，不可能一直等待网络返回，所以需要设置一个超时时间，这时候就可以使用 select timeout 模式，如下所示：

func main() {
   result := make(chan string)
   go func() {
      //模拟网络访问
      time.Sleep(8 * time.Second)
      result <- "服务端结果"
   }()
   select {
   case v := <-result:
      fmt.Println(v)
   case <-time.After(5 * time.Second):
      fmt.Println("网络访问超时了")
   }
}
select timeout 模式的核心在于通过 time.After 函数设置一个超时时间，防止因为异常造成 select 语句的无限等待。
小提示：如果可以使用 Context 的 WithTimeout 函数超时取消，要优先使用。
*/
